package com.hugang.juc.communication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 精确通知顺序访问
 *
 * 多线程之间按照顺序调用，实现A->B->C
 * 三个线程启动，要求如下：
 *  A线程打印5次，B打印10次，C打印15次
 *  接着
 *  A线程打印5次，B打印10次，C打印15次
 *  ...一共十轮
 *
 *  多线程操作方法：
 *   1、高内聚低耦合前提下，线程操作资源类
 *   2、判定 - 干活 - 通知
 *   3、多线程交互中，必须要防止多线程的虚假唤醒，即判断使用while，不使用if
 *   4、标志位
 * @author hg
 * @date 2020/8/19 15:47
 */
public class TestOrderAccess {

    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                shareResource.print5();
            }
        },"线程A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                shareResource.print10();
            }
        },"线程B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                shareResource.print15();
            }
        },"线程C").start();
    }
}

/**
 * 资源类
 */
class ShareResource{

    //标识位 1-A 2-B 3-C
    int flag = 1;
    //可重入锁
    Lock lock = new ReentrantLock();
    //影响因素 1-A 2-B 3-C
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    /**
     * A线程执行的方法
     */
    public void print5(){
        lock.lock();
        try {
            //1、判断
            while (flag != 1){
                //等待
                condition1.await();
            }
            //2、干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "---> " + i);
            }
            //3、通知 修改标志位、精确通知
            flag = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * B线程执行的方法
     */
    public void print10(){
        lock.lock();
        try {
            //1、判断
            while (flag != 2){
                //等待
                condition2.await();
            }
            //2、干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "---> " + i);
            }
            //3、通知 修改标志位、精确通知
            flag = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * C线程执行的方法
     */
    public void print15(){
        lock.lock();
        try {
            //1、判断
            while (flag != 3){
                //等待
                condition3.await();
            }
            //2、干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "---> " + i);
            }
            //3、通知 修改标志位、精确通知
            flag = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
